//
// Created on 2022/5/10.
//

#include "napi/native_api.h"
#include <cstdio>
#include <cstring>
#include <ctime>
#include <js_native_api.h>
#include <js_native_api_types.h>
#include <stdlib.h>
#include <string>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <hilog/log.h>
#include <pthread.h>
#include <cstring>
#include <iostream>

//定义了一些缓冲区的大小。
#define KEY_BUFFER_SIZE 20
#define DATA_BUFFER_SIZE 1024

//用于打印日志的设置，与通信功能无关。
#define LOG_DOMAIN 0x00201
#define LOG_TAG "MY_TAG"

//使用全局变量创建socket，防止被回收。
static int32_t sock_cli = socket(AF_INET,SOCK_STREAM, 0);

//开辟出js与c++的共享内存，c++中每次收到信息将其存到此内存中，js采用轮询方式去读取该内存中的消息。如果内存中为"#none#"，则无消息，不需要处理。
//注：此块内存不允许直接访问，通过setSharedMemory(),getSharedMemory()访问。
static char sharedMemory[1024] = "#none#";

//赋值给共享内存，仅供C++调用。
static void setSharedMemory(char* buf){
    strcpy(sharedMemory, buf);
}

//读取共享内存内容，仅供js调用。
static napi_value getSharedMemory(napi_env env, napi_callback_info info)
{
    napi_value result;
    napi_create_string_utf8(env, sharedMemory, sizeof(sharedMemory), &result);
    return result;
}

//开启线程接收消息。
static void *do_thread(void * arg){
    int ret ;
    char buffer[1024] = {0};
    while(1)
        {
            //打印日志代码。
            OH_LOG_INFO(LOG_APP,"子线程等待接收消息");

            //socket接收消息
            ret = read(sock_cli , buffer , 1024);
            if(ret <= 0)
                break;
            buffer[ret] = '\0' ;

            //将读到的消息写入共享内存。
            setSharedMemory(buffer);

            //打印日志代码。
            OH_LOG_INFO(LOG_APP,"%{public}s",&buffer);
            OH_LOG_INFO(LOG_APP,"%{public}s",&sharedMemory);
        }
}


//开启接收线程，执行do_thread函数。
static void startRec(){
    pthread_t tid;
    pthread_create(&tid, NULL, do_thread, (void*)sock_cli);
}


//启动socket连接，供js调用。
/**
 * 
 * @param env napi默认参数
 * @param info napi默认参数
 * @return 连接成功返回1，失败返回0。
 */
static napi_value Connect(napi_env env, napi_callback_info info)
{
    OH_LOG_INFO(LOG_APP,"进入连接函数，该日志仅代表成功调用了connect方法，不代表连接成功，供测试用。");

    //获取napi参数到args[2]
    size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    //获取参数，必写
    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);

    //获得IP地址
    size_t keyLen = 0;
    char address[KEY_BUFFER_SIZE];
    napi_get_value_string_utf8(env, args[0], address, sizeof(address), &keyLen);

    //获得端口号
    uint32_t port;
    napi_get_value_uint32(env, args[1], &port);
    uint16_t Port = port;


    ///定义sockaddr_in
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(Port);  //服务器端口
    servaddr.sin_addr.s_addr = inet_addr(address);  //服务器ip，inet_addr用于IPv4的IP转换（十进制转换为二进制）

    //连接服务器
    napi_value result;

    if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
    {
        napi_create_int32(env, 0, &result);
    }
    else {
        napi_create_int32(env, 1, &result);
        startRec();
    }

    //返回连接状态
    return result;
}


//发送消息，供js调用。
/**
 * 
 * @param env napi默认参数
 * @param info napi默认参数
 * @return 发送成功返回1，失败返回0。
 */
static napi_value Send(napi_env env, napi_callback_info info)
{
    //获取napi参数到args[2]
    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = {nullptr};

    //获取参数，必写
    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);

    //获取消息
    size_t keyLen = 0;
    char data[DATA_BUFFER_SIZE];
    napi_get_value_string_utf8(env, args[0], data, sizeof(data), &keyLen);

    napi_value result;

    //发送消息。
    if(send(sock_cli, data, strlen(data),0) == -1){
        napi_create_int32(env, 0, &result);
    } else {
        napi_create_int32(env, 1, &result);
    }
    //返回发送状态
    return result;
}

//socket关闭。
static napi_value Close(napi_env env, napi_callback_info info)
{
    close(sock_cli);
}

//static napi_value Receive(napi_env env, napi_callback_info info)
//{
//
////    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
//
//    struct sockaddr_in addr;
//    addr.sin_family = AF_INET;
//    addr.sin_port = htons(7000);
//    addr.sin_addr.s_addr = INADDR_ANY;
//
//    bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr));
//
//    listen(listen_fd, 5);
//
//    int conn;
//    char clientIP[INET_ADDRSTRLEN] = "";
//    struct sockaddr_in clientAddr;
//    socklen_t clientAddrLen = sizeof(clientAddr);
//
//    conn = accept(listen_fd, (struct sockaddr*)&clientAddr, &clientAddrLen);
//
//    inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, INET_ADDRSTRLEN);
//
//    char buf[255];
//    memset(buf, 0, sizeof(buf));
//    int len = recv(conn, buf, sizeof(buf), 0);
//    buf[len] = '\0';
//
//
//    napi_value result;
//
//    napi_create_string_utf8(env, buf, sizeof(buf), &result);
////    close(listenfd);
//    //返回连接状态
//    return result;
//}




//napi对外（js）暴露的方法。{connect,send,close,receive}
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
        { "connect", nullptr, Connect, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "send", nullptr, Send, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "close", nullptr, Close, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "receive", nullptr, getSharedMemory, nullptr, nullptr, nullptr, napi_default, nullptr },
//        { "receive", nullptr, Receive, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

//固定写法。（libsocket可改动根据具体自定义库名）
static napi_module demoModule = {
.nm_version =1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "libsocket",
.nm_priv = ((void*)0),
.reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterModule(void)
{
napi_module_register(&demoModule);
}
